package com.light.util;

import com.light.component.CustomProgressMonitor;
import com.light.exception.AuthException;
import com.light.exception.JGitException;
import com.light.exception.TimeOutException;
import com.light.model.GitAuthInfo;
import com.light.model.GitProject;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.PullCommand;
import org.eclipse.jgit.api.PullResult;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

public class JGitUtils {
    public static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class);

    // 存放gitee、GitHub用户名密码
    public static final Map<String, GitAuthInfo> AUTH_INFO_MAP = new HashMap<>();

    // 存放解析出来的本地项目
    private static final List<File> PROJECT_FILE = new ArrayList<>();

    // github和gitee更新失败次数，超过10次则不更新
    public static final AtomicInteger GITHUB_FAIL_NUMBER = new AtomicInteger(0);
    public static final AtomicInteger GITEE_FAIL_NUMBER = new AtomicInteger(0);

    /**
     * 凭证管理
     *
     * @param userName
     * @param password
     * @return
     */
    public static CredentialsProvider createCredential(String userName, String password) {
        return new UsernamePasswordCredentialsProvider(userName, password);
    }

    public static GitAuthInfo isExistsAuthInfo(String remoteUrl) {
        String type = getType(remoteUrl);
        if ("other".equals(type)) {
            return new GitAuthInfo(0, "other", "test", "test", "");
        } else {
            GitAuthInfo authInfo = AUTH_INFO_MAP.get(type);
            if (authInfo == null) {
                authInfo = H2PoolUtils.queryAuthInfo(type);
                AUTH_INFO_MAP.put(type, authInfo);
            }
            return authInfo;
        }
    }

    public static String getType(String remoteUrl) {
        if (remoteUrl.contains("github")) {
            return "Github";
        } else if (remoteUrl.contains("gitee")) {
            return "Gitee";
        }
        return "other";
    }

    /**
     * 获取项目名称
     *
     * @param remoteUrl
     * @return
     */
    public static String getRepoName(String remoteUrl) {
        String name = "";
        if (remoteUrl.endsWith(".git") || remoteUrl.endsWith(".gi") || remoteUrl.endsWith(".g") || remoteUrl.endsWith(".")) {
            name = remoteUrl.substring(remoteUrl.lastIndexOf("/") + 1, remoteUrl.lastIndexOf("."));
        } else {
            name = remoteUrl.substring(remoteUrl.lastIndexOf("/") + 1);
        }
        return name;
    }

    /**
     * 获取项目作者
     *
     * @param remoteUrl
     * @return
     */
    public static String getAuthor(String remoteUrl) {
        String author = remoteUrl.substring(0, remoteUrl.lastIndexOf("/"));
        return author.substring(author.lastIndexOf("/") + 1);
    }

    /**
     * 解析指定目录下所有符合git要求的项目地址
     *
     * @param repo
     */
    public static void parseLocalProjectPath(File repo) {
        if (Objects.nonNull(repo) && repo.isDirectory()) {
            String absolutePath = repo.getAbsolutePath();
            String gitDir = absolutePath.substring(absolutePath.lastIndexOf(File.separator) + 1);
            switch (gitDir) {
                case ".git" -> PROJECT_FILE.add(repo);
                case ".idea", "src", ".vscode", "target", "out", ".gradle", "gradle", "build", "log", "logs", "system", ".vs", "obj", "bin", "Assets", ".gitea",
                        ".github", "tests", "doc", "tools", "scripts", "resources" -> {
                }
                default -> {
                    File[] files = repo.listFiles();
                    if (null != files) {
                        // 过滤目录下的所有文件，如果和.git匹配上了，说明这个项目是git项目，则后续不需要继续比对
                        boolean exists = Arrays.stream(files).anyMatch(file -> {
                            String subPath = file.getAbsolutePath();
                            String subGitDir = subPath.substring(subPath.lastIndexOf(File.separator) + 1);
                            return ".git".equals(subGitDir);
                        });
                        if (!exists) {
                            // 如果不存在匹配上的.git,则继续往下遍历查找
                            Arrays.stream(files).parallel().forEach(JGitUtils::parseLocalProjectPath);
                        } else {
                            Arrays.stream(files).filter(file -> {
                                String subPath = file.getAbsolutePath();
                                String subGitDir = subPath.substring(subPath.lastIndexOf(File.separator) + 1);
                                return ".git".equals(subGitDir);
                            }).findFirst().ifPresent(PROJECT_FILE::add);
                        }
                    }
                }
            }
        }
    }

    /**
     * 需要在方法 parseLocalProjectPath 之后执行，解析所有符合要求的项目并入库
     */
    public static void parseLocalProject() {
        PROJECT_FILE.forEach(path -> {
            try (Git git = Git.open(path)) {
                List<RemoteConfig> call = git.remoteList().call();
                if (CollectionUtils.isEmpty(call)) {
                    // 本地项目 - 不加载
                    return;
                }
                String remotePath = call.get(0).getURIs().get(0).toString();
                if (!remotePath.contains("/")) {
                    // 本地项目 - 不加载
                    LOGGER.warn(remotePath);
                    return;
                }
                String name;
                if (remotePath.contains(".") && remotePath.lastIndexOf(".") > remotePath.lastIndexOf("/")) {
                    name = remotePath.substring(remotePath.lastIndexOf("/") + 1, remotePath.lastIndexOf("."));
                } else {
                    name = remotePath.substring(remotePath.lastIndexOf("/") + 1);
                }
                String author = remotePath.substring(0, remotePath.lastIndexOf("/"));
                String branch = git.getRepository().getBranch();
                String absolutePath = path.getAbsolutePath();
                String local = absolutePath.substring(0, absolutePath.lastIndexOf(".") - 1);

                GitProject newProject = new GitProject(new SimpleIntegerProperty(0),
                        new SimpleStringProperty(name),
                        new SimpleStringProperty(author.substring(author.lastIndexOf("/") + 1)),
                        new SimpleStringProperty(branch),
                        DateUtils.formatDateTime(new Date()),
                        new SimpleStringProperty(DateUtils.formatDateTime(new Date())),
                        remotePath,
                        new SimpleStringProperty(local),
                        new SimpleStringProperty(),
                        new SimpleStringProperty(),
                        new SimpleIntegerProperty(0),
                        new SimpleDoubleProperty(0.0),
                        new SimpleBooleanProperty(false)
                );
                // 插入之前验证是否已经存在
                if (H2PoolUtils.existsGitProjects(newProject)) {
                    LOGGER.warn("项目 {}，作者 {} 已经存在，不再新增", name, author);
                    FxUtil.addNoticeList("项目 " + name + "，作者 " + author + " 已经存在，不再新增");
                    newProject = null;
                } else {
                    // 不存在则继续新增，并设置id
                    H2PoolUtils.insertProjectInfo(newProject);
                    FxApplicationContextUtils.GIT_PROJECT_OBSERVABLE_LIST.add(newProject);
                }
            } catch (Exception e) {
                LOGGER.error("本地项目 {} 解析失败：{}", path.getAbsolutePath(), e.getMessage());
                FxUtil.addNoticeList("本地项目 " + path.getAbsolutePath() + " 解析失败：" + e.getMessage());
            }
        });

        // 清空缓存
        PROJECT_FILE.clear();
    }

    public static String cloneRepo(String remoteUrl, File localRepoFile, CredentialsProvider provider, CustomProgressMonitor monitor) throws JGitException, AuthException, TimeOutException {
        try (Git git = Git.cloneRepository()
                .setCredentialsProvider(provider)
                .setURI(remoteUrl).setDirectory(localRepoFile).setProgressMonitor(monitor).call()) {
            return git.getRepository().getBranch();
        } catch (Exception e) {
            String message = e.getMessage();
            LOGGER.error("项目 {} 克隆异常：{}", remoteUrl, message);
            FxUtil.addNoticeList("项目 " + remoteUrl + " 克隆异常：" + message);
            if (message.contains("authorized") || message.contains("Authentication")) {
                // 权限异常
                throw new AuthException();
            } else if (message.contains("time out") || message.contains("timeout")) {
                // 超时异常
                throw new TimeOutException();
            }
            throw new JGitException();
        }
    }

    public static boolean pull(String remoteUrl, File localRepoFile, CredentialsProvider provider, SimpleDoubleProperty rate) throws JGitException, AuthException, TimeOutException {
        try (Git git = Git.open(localRepoFile)) {
            PullCommand pullCommand = git.pull();
            pullCommand.setTimeout(10);
            if (provider != null) {
                pullCommand.setCredentialsProvider(provider);
            }
            pullCommand.setProgressMonitor(new CustomProgressMonitor(null, rate));
            PullResult call = pullCommand.call();
            return call.isSuccessful();
        } catch (Exception e) {
            String message = e.getMessage();
            LOGGER.error("项目 {} 更新异常：{}", remoteUrl, message);
            FxUtil.addNoticeList("项目 " + remoteUrl + " 更新异常：" + message);
            if ("Github".equals(getType(remoteUrl))) {
                GITHUB_FAIL_NUMBER.incrementAndGet();
            }
            if (message.contains("authorized") || message.contains("Authentication")) {
                if ("Gitee".equals(getType(remoteUrl))) {
                    GITEE_FAIL_NUMBER.incrementAndGet();
                }
                // 权限异常
                throw new AuthException();
            } else if (message.contains("time out") || message.contains("timeout")) {
                if ("Gitee".equals(getType(remoteUrl))) {
                    GITEE_FAIL_NUMBER.incrementAndGet();
                }
                // 超时异常
                throw new TimeOutException();
            }
            throw new JGitException();
        }
    }

    public static List<String> branchs(File localRepoFile) throws JGitException {
        List<String> result = new ArrayList<>();
        try (Git git = Git.open(localRepoFile)) {
            ListBranchCommand listBranchCommand = git.branchList();
            List<Ref> callList = listBranchCommand.call();
            for (Ref ref : callList) {
                result.add(ref.getName().replace("refs/heads/", ""));
            }
        } catch (Exception e) {
            LOGGER.error("获取项目 {} 所有分支异常：{}", localRepoFile.getAbsolutePath(), e.getMessage());
            throw new JGitException(e);
        }
        return result;
    }

    public static void main(String[] args) {
        branchs(new File("D:\\workspace\\workspace-dev\\git-manager-client-fx")).forEach(System.out::println);
    }
}
